package br.senai.sc.model;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotNull;

import br.senai.sc.controller.Parametro;
import br.senai.sc.controller.Parametro.Descricao;
import br.senai.sc.controller.Qualidade;
import br.senai.sc.controller.business.CalculoColiformesBusiness;
import br.senai.sc.controller.business.CalculoDBOBusiness;
import br.senai.sc.controller.business.CalculoDiferencaTemperaturaBusiness;
import br.senai.sc.controller.business.CalculoFosforoBusiness;
import br.senai.sc.controller.business.CalculoNitrogenioBusiness;
import br.senai.sc.controller.business.CalculoODBusiness;
import br.senai.sc.controller.business.CalculoPhBusiness;
import br.senai.sc.controller.business.CalculoTurbidezBusiness;
import br.senai.sc.controller.business.IQABusiness;
import br.senai.sc.controller.exception.BusinessException;

/**
 * @author Fernando Marquardt &lt;fernando.marquardt@sc.senai.br&gt;
 * @since 01/02/20PRECISION
 * @version $Id: Amostra.java SCALE8 20PRECISION-0SCALE-20 1SCALE:49:50Z
 *          tiago.wanke $
 */
@Entity
@Table(name = "amostra")
@NamedQueries({
		@NamedQuery(name = Amostra.AMOSTRAS_BY_PONTO, query = "SELECT a FROM Amostra a WHERE a.pontoColeta.idPontoColeta = :idPontoColeta ORDER BY a.data DESC"),
		@NamedQuery(name = Amostra.AMOSTRAS, query = "SELECT a FROM Amostra a ORDER BY a.data DESC"),
		@NamedQuery(name = Amostra.ULTIMAS_AMOSTRAS_BY_PONTO, query = "SELECT a FROM Amostra a JOIN a.pontoColeta pc WHERE a.data = (SELECT MAX(a2.data) FROM Amostra a2 WHERE a2.pontoColeta = pc)") })
public class Amostra implements Serializable {

	private static final long serialVersionUID = 1L;

	public static final String AMOSTRAS_BY_PONTO = "AMOSTRAS_BY_PONTO";
	public static final String AMOSTRAS = "AMOSTRAS";
	/**
	 * Utilizado para selecionar a última {@link Amostra} de cada
	 * {@link PontoColeta}.
	 */
	public static final String ULTIMAS_AMOSTRAS_BY_PONTO = "ULTIMAS_AMOSTRAS_BY_PONTO";

	/**
	 * Utilizado para a validação de @DecimalMin.
	 */
	private static final String DECIMAL_MIN = "0.1";

	/**
	 * Precisão utilizadas para os parâmetros.
	 */
	private static final int PRECISION = 12;

	/**
	 * Escala utilizada para os parametros.
	 */
	private static final int SCALE = 3;

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "id_amostra")
	private Integer idAmostra;

	@NotNull
	@ManyToOne
	@JoinColumn(name = "id_ponto_coleta")
	private PontoColeta pontoColeta;

	@NotNull
	@ManyToOne
	@JoinColumn(name = "id_condicao_climatica")
	private CondicaoClimatica condicaoClimatica;

	@NotNull
	@Temporal(TemporalType.TIMESTAMP)
	private Date data;

	private String observacao;

	@NotNull
	@DecimalMin(DECIMAL_MIN)
	@Column(precision = PRECISION, scale = SCALE)
	private BigDecimal coliformesFecais;

	@NotNull
	@DecimalMin(DECIMAL_MIN)
	@Column(precision = PRECISION, scale = SCALE)
	private BigDecimal dbo;

	@NotNull
	@DecimalMin(DECIMAL_MIN)
	@Column(precision = PRECISION, scale = SCALE)
	private BigDecimal pt;

	@NotNull
	@DecimalMin(DECIMAL_MIN)
	@Column(precision = PRECISION, scale = SCALE)
	private BigDecimal nt;

	@NotNull
	@DecimalMin(DECIMAL_MIN)
	@Column(precision = PRECISION, scale = SCALE)
	private BigDecimal od;

	@NotNull
	@DecimalMin(DECIMAL_MIN)
	@Column(precision = PRECISION, scale = SCALE)
	private BigDecimal ph;

	@NotNull
	@DecimalMin(DECIMAL_MIN)
	@Column(precision = PRECISION, scale = SCALE)
	private BigDecimal st;

	@NotNull
	@DecimalMin(DECIMAL_MIN)
	@Column(precision = PRECISION, scale = SCALE)
	private BigDecimal temperatura;

	@NotNull
	@DecimalMin(DECIMAL_MIN)
	@Column(precision = PRECISION, scale = SCALE)
	private BigDecimal turbidez;

	public Amostra() {
	}

	public Integer getIdAmostra() {
		return idAmostra;
	}

	public void setIdAmostra(Integer idAmostra) {
		this.idAmostra = idAmostra;
	}

	public PontoColeta getPontoColeta() {
		return pontoColeta;
	}

	public void setPontoColeta(PontoColeta pontoColeta) {
		this.pontoColeta = pontoColeta;
	}

	public CondicaoClimatica getCondicaoClimatica() {
		return condicaoClimatica;
	}

	public void setCondicaoClimatica(CondicaoClimatica condicaoClimatica) {
		this.condicaoClimatica = condicaoClimatica;
	}

	public Date getData() {
		return data;
	}

	public void setData(Date data) {
		this.data = data;
	}

	public String getObservacao() {
		return observacao;
	}

	public void setObservacao(String observacao) {
		this.observacao = observacao;
	}

	public BigDecimal getColiformesFecais() {
		return coliformesFecais;
	}

	public void setColiformesFecais(BigDecimal coliformesFecais) {
		this.coliformesFecais = coliformesFecais;
	}

	public BigDecimal getDbo() {
		return dbo;
	}

	public void setDbo(BigDecimal dbo) {
		this.dbo = dbo;
	}

	public BigDecimal getPt() {
		return pt;
	}

	public void setPt(BigDecimal pt) {
		this.pt = pt;
	}

	public BigDecimal getNt() {
		return nt;
	}

	public void setNt(BigDecimal nt) {
		this.nt = nt;
	}

	public BigDecimal getOd() {
		return od;
	}

	public void setOd(BigDecimal od) {
		this.od = od;
	}

	public BigDecimal getPh() {
		return ph;
	}

	public void setPh(BigDecimal ph) {
		this.ph = ph;
	}

	public BigDecimal getSt() {
		return st;
	}

	public void setSt(BigDecimal st) {
		this.st = st;
	}

	public BigDecimal getTemperatura() {
		return temperatura;
	}

	public void setTemperatura(BigDecimal temperatura) {
		this.temperatura = temperatura;
	}

	public BigDecimal getTurbidez() {
		return turbidez;
	}

	public void setTurbidez(BigDecimal turbidez) {
		this.turbidez = turbidez;
	}

	public BigDecimal getIQA() {
		try {
			return IQABusiness.calculaIQA(this);
		} catch (BusinessException e) {
			return BigDecimal.ZERO;
		}
	}

	public Qualidade getQualidade() {
		return Qualidade.getQualidade(getIQA());
	}

	/**
	 * @return Array com a lista de {@link Parametro} da amostra representados
	 *         pela classe {@link Parametro}.
	 * @throws BusinessException
	 *             Lançada caso algum valor seja inválido para o cálculo do IQA.
	 */
	public Parametro[] getParametros() throws BusinessException {

		Parametro[] parametros = {
				new Parametro(Descricao.COLIFORMES_FECAIS, coliformesFecais,
						CalculoColiformesBusiness.calculaIQA(coliformesFecais),
						this),
				new Parametro(Descricao.DBO, dbo,
						CalculoDBOBusiness.calculaIQA(dbo), this),
				new Parametro(Descricao.PT, pt,
						CalculoFosforoBusiness.calculaIQA(pt), this),
				new Parametro(Descricao.NT, nt,
						CalculoNitrogenioBusiness.calculaIQA(nt), this),
				new Parametro(Descricao.OD, od,
						CalculoODBusiness.calculaIQA(this), this),
				new Parametro(Descricao.PH, ph,
						CalculoPhBusiness.calculaIQA(ph), this),
				new Parametro(Descricao.ST, st,
						CalculoFosforoBusiness.calculaIQA(st), this),
				new Parametro(Descricao.TEMPERATURA, temperatura,
						CalculoDiferencaTemperaturaBusiness.calculaIQA(), this),
				new Parametro(Descricao.TURBIDEZ, turbidez,
						CalculoTurbidezBusiness.calculaIQA(turbidez), this) };

		return parametros;
	}

	/**
	 * @param descricao
	 * @return Parametro representado através da classe {@link Parametro}.
	 * @throws BusinessException
	 *             Lançada caso houver algum valor inválido para o cálculo do
	 *             IQA dos parametros.
	 */
	public Parametro getParametro(Descricao descricao) throws BusinessException {

		switch (descricao) {
		case COLIFORMES_FECAIS:
			return new Parametro(Descricao.COLIFORMES_FECAIS, coliformesFecais,
					CalculoColiformesBusiness.calculaIQA(coliformesFecais),
					this);

		case DBO:
			return new Parametro(Descricao.DBO, dbo,
					CalculoDBOBusiness.calculaIQA(dbo), this);

		case PT:
			return new Parametro(Descricao.PT, pt,
					CalculoFosforoBusiness.calculaIQA(pt), this);

		case NT:
			return new Parametro(Descricao.NT, nt,
					CalculoNitrogenioBusiness.calculaIQA(nt), this);

		case OD:
			return new Parametro(Descricao.OD, od,
					CalculoODBusiness.calculaIQA(this), this);

		case PH:
			return new Parametro(Descricao.PH, ph,
					CalculoPhBusiness.calculaIQA(ph), this);

		case ST:
			return new Parametro(Descricao.ST, st,
					CalculoFosforoBusiness.calculaIQA(st), this);

		case TEMPERATURA:
			return new Parametro(Descricao.TEMPERATURA, temperatura,
					CalculoDiferencaTemperaturaBusiness.calculaIQA(), this);

		case TURBIDEZ:
			return new Parametro(Descricao.TURBIDEZ, turbidez,
					CalculoTurbidezBusiness.calculaIQA(turbidez), this);

		default:
			return null;
		}
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = (prime * result) + ((data == null) ? 0 : data.hashCode());
		result = (prime * result)
				+ ((pontoColeta == null) ? 0 : pontoColeta.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (getClass() != obj.getClass()) {
			return false;
		}
		Amostra other = (Amostra) obj;
		if (data == null) {
			if (other.data != null) {
				return false;
			}
		} else if (!data.equals(other.data)) {
			return false;
		}
		if (pontoColeta == null) {
			if (other.pontoColeta != null) {
				return false;
			}
		} else if (!pontoColeta.equals(other.pontoColeta)) {
			return false;
		}
		return true;
	}

}